#!/usr/bin/perl
use warnings;
use strict;
use File::stat;
use Tie::File;
#******************************************************************************
# File: 
#		perl.pl
# Recipe: 8.4
#		Reading a File Backward by Line or Paragraph
# Version:
#		0.1
# Author:
#   	Yubei Zhu
# Mail:
#		yubeizhu@gmail.com
# Created at:
#   	2012-08-20  
# Last Update:
#   	2012-08-20
# License: 
#		GPLv3	
#******************************************************************************

############################### Function Part ################################

sub IsReadableFile
#*****************************************************************************
# PURPOSE: 
#      Testing whether a file is readable
# USAGE:   
#      my $sStatus = IsReadableFile( $sFileName )
# PASSED ARGS:  
#      Scalar: filename
# RETURN ARGS:
# 	   True or false
# $sError - Fail Error Message
#       None
#******************************************************************************
{
	my $sFileName = $_[ 0 ];
	return -f $sFileName and -r _ ? 1 : 0;
}

sub IsStableFile
#*****************************************************************************
# PURPOSE: 
#       Testing whether a file is stable or not
# USAGE:   
#      my $sStatus = IsStableFile( $sFileName )
# PASSED ARGS:  
#      Scalar: filename
# RETURN ARGS:
# 	   True or false
# $sError - Fail Error Message
#       None
#******************************************************************************
{
	my $sFileName = shift;
	my $sAgingSec = $_[ 0 ] || 60;
	
	return time() - stat( $sFileName )->mtime > $sAgingSec ? 1 : 0;
}


sub PopReverse
#*****************************************************************************
# PURPOSE: 
#       Reading a File Backward by Line or Paragraph
# USAGE:   
#      my $raLines = PopReverse( $sFileName )
# PASSED ARGS:  
#      Scalar: filename
# RETURN ARGS:
#      list reference: file content. Or 0 if file is invalid
# $sError - Fail Error Message
#       None
#******************************************************************************
{
	my $sFileName = shift;
	return 0 if not IsReadableFile( $sFileName ) or not IsStableFile( $sFileName );

	open my $fhIn,"<",	$sFileName or die "Can't open file $sFileName:$!";
	my @aLines = <$fhIn>; # List context
	close( $fhIn ) or die "Can't close file $sFileName:$!";
	
	my @aRLines;
	push @aRLines, pop @aLines while @aLines;	# Destruct original array!1`
	return \@aRLines;
}


sub SubReverse
#*****************************************************************************
# PURPOSE: 
#       Reading a File Backward by Line or Paragraph
# USAGE:   
#      my $raLines = PopReverse( $sFileName )
# PASSED ARGS:  
#      Scalar: filename
# RETURN ARGS:
#      list reference: file content. Or 0 if file is invalid
# $sError - Fail Error Message
#       None
#******************************************************************************
{
	my $sFileName = shift;
	return 0 if not IsReadableFile( $sFileName ) or not IsStableFile( $sFileName );
	open my $fhIn,"<", $sFileName or die "Can't open file $sFileName:$!";
	my @aLines = reverse <$fhIn>; # List context
	close( $fhIn) or die "Can't close file $sFileName:$!";
	
	return \@aLines;
}

sub TieReverse
#*****************************************************************************
# PURPOSE: 
#       Reading a File Backward by Line or Paragraph
# USAGE:   
#      my $raLines = TieReverse( $sFileName )
# 	   my $raLines = TieReverse( $sFileName, 0 )
# PASSED ARGS:  
#      Scalar: filename
#	   Scalar(optional): autochomp flag, 1 by default
# RETURN ARGS:
#      list reference: file content. Or 0 if file is invalid
# $sError - Fail Error Message
#       None
#******************************************************************************
{
	my $sFileName = shift;
	# By default, we chomp eachline in array
	my $sDoChomp = defined $_[ 0 ] ? $_[ 0 ] : 1;
	
	return 0 if not IsReadableFile( $sFileName ) or not IsStableFile( $sFileName );
	my @aLines;
	# Readonly
	tie( @aLines, "Tie::File", $sFileName, mode => 0, autochomp => $sDoChomp  );
	eval{
		shift @aLines;# Delete first line
	};
	print "Issue: you can't modify data in Tie::File:$@" if($@);


	
	my @aRLines = reverse @aLines;
	return \@aRLines
}

sub TieReverseRDWR
#*****************************************************************************
# PURPOSE: 
#       Reading a File Backward by Line or Paragraph
# USAGE:   
#      my $raLines = TieReverseRDWR( $sFileName )
# 	   my $raLines = TieReverseRDWR( $sFileName, 0 )
# PASSED ARGS:  
#      Scalar: filename
#	   Scalar(optional): autochomp flag, 1 by default
# RETURN ARGS:
#      list reference: file content. Or 0 if file is invalid
# $sError - Fail Error Message
#       None
#******************************************************************************
{
	my $sFileName = shift;
	# By default, we chomp eachline in array
	my $sDoChomp = defined $_[ 0 ] ? $_[ 0 ] : 1;
	
	return 0 if not IsReadableFile( $sFileName ) or not IsStableFile( $sFileName );
	my @aLines;

	# RDRW Mode
	tie( @aLines, "Tie::File", $sFileName,  autochomp => $sDoChomp  );
	eval{
		shift @aLines;# Delete first line
	};
	print "Issue: you can't modify data in Tie::File:$@" if($@);
	
	my @aRLines = reverse @aLines;
	return \@aRLines
}

################################# Main Part ################################### 
# Test file
my $sTestFile = "D:/personal/test.dat";

# We don't validate the result here
my $raLines = SubReverse( $sTestFile );
print "SubReverse:\n", @{ $raLines };
print "======\n";

$raLines = PopReverse( $sTestFile );
print "PopReverse:\n", @{ $raLines };
print "======\n";

$raLines = TieReverse( $sTestFile );
print "TieReverse and no chomp:\n",join("\n", @{ $raLines } );
print "======\n";

$raLines = TieReverse( $sTestFile, 0  );
print "TieReverse and chomp:\n", @{ $raLines };
print "======\n";

$raLines = TieReverseRDWR( $sTestFile, 0 );
# We also modify the file here 
print "TieReverseRDWR and chomp:\n", @{ $raLines };